package com.jnc.core.config.jdbc.aspect;


import com.jnc.core.config.jdbc.annotation.MultiTransaction;
import com.jnc.core.util.SpringUtil;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.util.Stack;

/**
 * @Auther: jjn
 * @Date: 2018/11/4
 * @Desc:
 */
@Aspect
@Component
public class MultiTransactionAspect {
    private Logger logger = LoggerFactory.getLogger(MultiTransactionAspect.class);

    /**
     * 使用切面模拟编程式事务，实现多数据源的分布式事务
     * @param joinPoint
     * @param multiTransaction
     * @return
     * @throws Throwable
     */
    @Around("@annotation(multiTransaction)")
    public Object around(ProceedingJoinPoint joinPoint, MultiTransaction multiTransaction) throws Throwable{
        Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack = new Stack<DataSourceTransactionManager>();
        Stack<TransactionStatus> transactionStatuseStack = new Stack<TransactionStatus>();

        try{
            if(!openTransaction(dataSourceTransactionManagerStack,
                    transactionStatuseStack, multiTransaction)){
                return null;
            }
            Object obj = joinPoint.proceed();
            //提交事务
            commit(dataSourceTransactionManagerStack, transactionStatuseStack);
            return obj;
        }catch (Throwable e){
            rollback(dataSourceTransactionManagerStack, transactionStatuseStack);
            logger.error("MultiTransactionAspect, method {}-{} proceed error: {}", joinPoint.getTarget().getClass().getSimpleName(),
                    joinPoint.getSignature().getName(), ExceptionUtils.getStackTrace(e));
            throw e;
        }
    }

    /**
     * 开启多事务管理
     * @param dataSourceTransactionManagerStack
     * @param transactionStatuseStack
     * @param multiTransaction
     * @return
     */
    private boolean openTransaction(
            Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
            Stack<TransactionStatus> transactionStatuseStack,
            MultiTransaction multiTransaction){
        String[] transactionManagerNames = multiTransaction.values();
        if(ArrayUtils.isEmpty(multiTransaction.values())){
            return false;
        }

        for (String beanName : transactionManagerNames){
            DataSourceTransactionManager dataSourceTransactionManager = SpringUtil.getBean(beanName);
            TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(new DefaultTransactionDefinition());
            transactionStatuseStack.push(transactionStatus);
            dataSourceTransactionManagerStack.push(dataSourceTransactionManager);
        }
        return true;
    }

    /**
     * 事务提交
     * @param dataSourceTransactionManagerStack
     * @param transactionStatuseStack
     */
    private void commit(
            Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
            Stack<TransactionStatus> transactionStatuseStack){
        while (!dataSourceTransactionManagerStack.isEmpty()){
            dataSourceTransactionManagerStack.pop().commit(transactionStatuseStack.pop());
        }
    }

    /**
     * 事务回滚
     * @param dataSourceTransactionManagerStack
     * @param transactionStatuseStack
     */
    public void rollback(
            Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
            Stack<TransactionStatus> transactionStatuseStack){
        while (!dataSourceTransactionManagerStack.isEmpty()){
            dataSourceTransactionManagerStack.pop().rollback(transactionStatuseStack.pop());
        }
    }
}
